/*******************************************************************************
 * Copyright (c) 2009, 2013 Mountainminds GmbH & Co. KG and Contributors
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    Marc R. Hoffmann - initial API and implementation
 *    
 *******************************************************************************/
package com.mountainminds.eclemma.autoMerge;

import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import org.jacoco.core.analysis.IBundleCoverage;
import org.jacoco.core.analysis.IClassCoverage;
import org.jacoco.core.analysis.ICoverageNode;
import org.jacoco.core.analysis.ICoverageVisitor;
import org.jacoco.core.analysis.IPackageCoverage;
import org.jacoco.core.analysis.ISourceFileCoverage;
import org.jacoco.core.internal.analysis.BundleCoverageImpl;
import org.jacoco.core.internal.analysis.SourceFileCoverageImpl;

/**
 * Builder for hierarchical {@link ICoverageNode} structures from single
 * {@link IClassCoverage} nodes. The nodes are feed into the builder through its
 * {@link ICoverageVisitor} interface. Afterwards the aggregated data can be
 * obtained with {@link #getClasses()}, {@link #getSourceFiles()} or
 * {@link #getBundle(String)} in the following hierarchy:
 * 
 * <pre>
 * {@link IBundleCoverage}
 * +-- {@link IPackageCoverage}*
 *     +-- {@link IClassCoverage}*
 *     +-- {@link ISourceFileCoverage}*
 * </pre>
 */
public class CoverageBuilder implements ICoverageVisitor {

  private final Map<String, IClassCoverage> classes;

  private final Map<String, ISourceFileCoverage> sourcefiles;

  /**
   * Create a new builder.
   * 
   */
  public CoverageBuilder() {
    this.classes = new HashMap<String, IClassCoverage>();
    this.sourcefiles = new HashMap<String, ISourceFileCoverage>();
    // System.err.println("get.....MyCoverageBuilder");
  }

  /**
   * Returns all class nodes currently contained in this builder.
   * 
   * @return all class nodes
   */
  public Collection<IClassCoverage> getClasses() {
    return Collections.unmodifiableCollection(classes.values());
  }

  /**
   * Returns all source file nodes currently contained in this builder.
   * 
   * @return all source file nodes
   */
  public Collection<ISourceFileCoverage> getSourceFiles() {
    return Collections.unmodifiableCollection(sourcefiles.values());
  }

  /**
   * Creates a bundle from all nodes currently contained in this bundle.
   * 
   * @param name
   *          Name of the bundle
   * @return bundle containing all classes and source files
   */
  public IBundleCoverage getBundle(final String name) {
    return new BundleCoverageImpl(name, classes.values(), sourcefiles.values());
  }

  // === IStructureVisitor ===

  public void visitCoverage(final IClassCoverage coverage) {
    // Only consider classes that actually contain code:
    if (coverage.getInstructionCounter().getTotalCount() > 0) {
      final String name = coverage.getName();
      // System.err.println("visitCoverage:" + coverage.getName());
      final IClassCoverage dup = classes.put(name, coverage);
      if (dup != null && dup.getId() != coverage.getId()) {
        throw new IllegalStateException(
            "Can't add different class with same name: " + name); //$NON-NLS-1$
      }
      final String source = coverage.getSourceFileName();
      if (source != null) {
        final SourceFileCoverageImpl sourceFile = getSourceFile(source,
            coverage.getPackageName());
        sourceFile.increment(coverage);
      }
    }
  }

  private SourceFileCoverageImpl getSourceFile(final String filename,
      final String packagename) {
    final String key = packagename + '/' + filename;
    SourceFileCoverageImpl sourcefile = (SourceFileCoverageImpl) sourcefiles
        .get(key);
    if (sourcefile == null) {
      sourcefile = new SourceFileCoverageImpl(filename, packagename);
      sourcefiles.put(key, sourcefile);
    }
    return sourcefile;
  }

}
